<?php
/**
 * User: lishu
 */

///// TODO: 
/////   heart's aim
/////   部分匹配失败后的回溯

if (! function_exists('pos_in_sentence')) {
	function pos_in_sentence($phrase, $sentence, $fromWordInSentence = 0)
	{
		$fDebug = false;
		$maxDistance = 5;
		
		$phrase = strtolower($phrase);
		$sentence = strtolower($sentence);
		
		$rawWordInPhrase = explode(" ",$phrase);
//		$sentence2 = preg_replace(" , | . | ? | \" | \' | : | ! | - "," ",$sentence);
		$punctures = array(",",".","?","!",":",'"',"'","-",";","[","]");
		$sentence2 = $sentence;
		foreach($punctures as $puncture){
			$sentence2 = str_replace($puncture," ",$sentence2);
		}
		$wordInSentence = explode(" ", $sentence2);
		
		$wordInPhrase = array();
		$maxMandatoryIndex = count($rawWordInPhrase);
		for($i=0;$i<count($rawWordInPhrase);$i++){
			$curWord = $rawWordInPhrase[$i];
			$wordInPhrase[$i]['variants'] = getWordVariants($curWord);
			for($j=0;$j<count($wordInPhrase[$i]['variants']);$j++)
				$wordInPhrase[$i]['variants'][$j] = strtolower($wordInPhrase[$i]['variants'][$j]);
			$wordInPhrase[$i]['type'] = getWordOptionalType($curWord);
			if ($wordInPhrase[$i]['type']=='mandatory'){
				$maxMandatoryIndex = $i;
			}
		}
		//var_dump($wordInPhrase);
		if ($fDebug) echo "maxMandatoryIndex=$maxMandatoryIndex<br>";

	 	//需求：
	 	//  (1)支持单词变形
	 	//  (2)假设词组在句中保持顺序，即假设词组为”word1 word2 word3"，只在句中查找"blah... word1 blah... word2 blah... word3"
	 	//  (3)词组间的两个相邻单词在句中最多插入maxDistance-1个单词
	 	//  (4)忽略大小写
	 	//  (5)支持预定义的可选匹配，例如定冠词、不定冠词、所有格。如果匹配，则标出；如果没找到匹配，不影响最后结果
	 	//要求：
	 	//  (1)句子中的",",".","?","!",":",'"',"'","-"都视为单词的间隔符，实现中转化成空格
	 	//  (2)模式中的单词严格以一个英文空格间隔（TODO:加入错误检查？）		
		
		//构造状态机
		//state表示将要匹配$wordInPhrase[state][variants],但还没有匹配上
		//state 遍历模式，
		//  wordIndex 从stateIndex...模式结束
		//	读入一个wordInPhrase，
		//	加入transition[state] []={'input'=>wordVariant,'newState'=}
		//	如果是mandatory break;		
		$fSM = array();
		for($state=0;$state<count($rawWordInPhrase);$state++){
			$fSM[$state] = array();
			$transition=0;
			for($wordIndex=$state;$wordIndex<count($rawWordInPhrase);$wordIndex++,$transition++){
				$fSM[$state][$transition]['variants'] = $wordInPhrase[$wordIndex]['variants']; //可以优化，去掉和$fSM[$state][$transition-1]重复的
				$fSM[$state][$transition]['newState'] = $wordIndex+1;
				if ($wordInPhrase[$wordIndex]['type']=='mandatory')
					break;
			}
		}
		if($fDebug){
			for($state=0;$state<count($rawWordInPhrase);$state++){
				if ($fDebug) echo "state=$state</br>".print_r($fSM[$state],true)."</br>";
			}
			if ($fDebug) echo "----------------</br>";
		}
		//exit;
		
		//按照状态机进行匹配
		//当前state=0
		//index 遍历句子
		//do{
		//  遍历transition[state]
		//    如果匹配并且满足distance条件，break;
		//  
		//  如果跳出遍历transition[state]循环的原因：匹配
		//    如果已经到了最后一个强制匹配，标记匹配成功
		//	加到foundList
		//	状态转换
		//	    如果当前状态超出有效范围，break
		//	    如果当前状态是强制状态(newState-1),则更新maxWordIndexInSentence
		//  如果跳出遍历transition[state]循环的原因：不匹配
		//    break
		//}
		$foundAllWord = false;
		for($ii=$fromWordInSentence;$ii<count($wordInSentence);$ii++){
		//for($ii=0;$ii<1;$ii++){	
			if ($fDebug) echo "ii=$ii</br>";
			$state=0;
			$foundAllWord = false;
			$foundList = array();
			$maxWordIndexInSentence = count($wordInSentence)-1;
			for($i=$ii;$i<min(count($wordInSentence),$maxWordIndexInSentence+1);$i++){
				$foundCurWord=false;
				$curWordInSentence = $wordInSentence[$i];
				if ($fDebug)  echo "curWordInSentence=$curWordInSentence</br>";
				for($j=0;$j<count($fSM[$state]);$j++){
					if(in_array($curWordInSentence, $fSM[$state][$j]['variants'])){
						$foundCurWord=true;
						break;
					}
				}
				if($foundCurWord==true){
					$foundList []= array("startWordPos"=>$i,"len"=>strlen($wordInSentence[$i]));
					$newState = $fSM[$state][$j]['newState'];
					if ($fDebug) echo "state=$state j=$j newState=$newState</br>";
					if($state==$maxMandatoryIndex){
						$foundAllWord = true;
					}
					if($newState>=count($fSM)){
						break;
					}
					if($wordInPhrase[$state]['type']=='mandatory'){
						$maxWordIndexInSentence = $i + $maxDistance;
						if($fDebug)  echo "maxWordIndexInSentence=$maxWordIndexInSentence</br>";
					}
					$state=$newState;
				}
			}
			if ($foundAllWord==true){
				break;
			}
		}

	 	if(!isset($foundAllWord)){
			echo "rainy day scenario foundAllWord not set</br>";
		}
		if($foundAllWord==false){
	 		if ($fDebug) echo "not found";
			return array();
	 	}else{
	 		if ($fDebug) echo "found: ";
	 		if ($fDebug) var_dump($foundList);
			
	 		$letterPosInSentence=array(0);
	 		for($i=1;$i<count($wordInSentence);$i++){
	 			$letterPosInSentence[$i] = $letterPosInSentence[$i-1] + mb_strlen($wordInSentence[$i-1]) +1;
	 		}
	 		if ($fDebug)  var_dump($letterPosInSentence);
	 		if ($fDebug)  var_dump($foundList);
	 		for($i=0;$i<count($foundList);$i++){
	 			if ($fDebug)  echo $foundList[$i]['startWordPos']."  ";
	 		}
     
	 		for($i=0;$i<count($foundList);$i++){
	 			if ($fDebug)  echo substr($sentence,$letterPosInSentence[$foundList[$i]['startWordPos']],strlen($wordInSentence[$foundList[$i]['startWordPos']]))." ";
				$foundList[$i]['start'] = $letterPosInSentence[$foundList[$i]['startWordPos']];
	 		}
	 		if ($fDebug)  echo "</br>";
			return $foundList;
	 	}
	}
	//土方法，除了optional之外，都可以工作
	//{
	//	$fDebug = true;
	//	$maxDistance = 5;
	//	
	//	$phrase = strtolower($phrase);
	//	$sentence = strtolower($sentence);
	//	
	//	$wordInPhrase = explode(" ",$phrase);
//	//	$sentence2 = preg_replace(" , | . | ? | \" | \' | : | ! | - "," ",$sentence);
	//	$punctures = array(",",".","?","!",":",'"',"'","-");
	//	$sentence2 = $sentence;
	//	foreach($punctures as $puncture){
	//		$sentence2 = str_replace($puncture," ",$sentence2);
	//	}
	//	$wordInSentence = explode(" ", $sentence2);
    //
	//	$mandatoryWordIdx = -1;
	//	$mandatoryWordList = array();
	//	$firstOptionalIndexList = array();
	//	foreach($wordInPhrase as $k=>$word){
	//		$optionalType = getWordOptionalType($word);
	//		if ($optionalType == 'mandatory'){
	//			$mandatoryWordIdx++;
	//			$mandatoryIndexList[$mandatoryWordIdx]['self']= $k;
	//		} else{
	//			if ($mandatoryWordIdx==-1){
	//				$firstOptionalIndexList []= $k;
	//			}else{
	//				$mandatoryIndexList[$mandatoryWordIdx]['after'] []= $k;
	//			}
	//		}
	//	}
	//	if ($fDebug)  var_dump($mandatoryIndexList);
	//	if ($fDebug)  echo count($mandatoryIndexList);
	//	if ($fDebug)  var_dump($firstOptionalIndexList);
	//	
	//	if ($fDebug)  var_dump($wordInSentence);
	//	if ($fDebug)  var_dump($wordInPhrase);
	//	
	//	//需求：
	//	//  (1)支持单词变形
	//	//  (2)假设词组在句中保持顺序，即假设词组为”word1 word2 word3"，只在句中查找"blah... word1 blah... word2 blah... word3"
	//	//  (3)词组间的两个相邻单词在句中最多插入maxDistance-1个单词
	//	//  (4)忽略大小写
	//	//  (5)支持预定义的可选匹配，例如定冠词、不定冠词、所有格。如果匹配，则标出；如果没找到匹配，不影响最后结果
	//	//要求：
	//	//  (1)句子中的",",".","?","!",":",'"',"'","-"都视为单词的间隔符，实现中转化成空格
	//	//  (2)模式中的单词严格以一个英文空格间隔（TODO:加入错误检查？）
	//	//说明：
	//	//  本注释中的wordInPhrase都是mandatory
	//	//三重循环，循环变量分别是idx1,idx2和idx3
	//	//idx1，最外层循环，遍历wordInSentence
	//	//   终止：count($wordInSentence)-count($wordInPhrase)+1，即第一个单词在句中最后可能的位置。
	//	//   break跳出：如果在中层循环找到所有单词，则置foundAllWord，并且break
	//	//   如果在内层循环找到第一个单词，则置foundFirstWord和foundFirstWordPos，idx1的下一个循环可以从foundFirstWordPos+1
	//	//idx2, 中层循环，遍历wordInPhrase，查找每个单词
	//	//idx3, 内层循环，遍历wordInSentence，查找wordInPhrase[idx2]或者其变形
	//	//   如果idx2==0
    //    //     循环开始：idx1
	//	//     循环结束：第idx2+1个单词在句中可能最后的位置
	//	//   如果idx2>0
    //    //     循环开始：第idx2个单词（即上一个找到的单词）位置+1
	//	//     循环结束：min(第idx2+1个单词在句中可能最后的位置,第idx2个单词（即上一个找到的单词）位置+maxDistance)
	//	//   break跳出：如果在本层循环找到指定单词，则置foundCurWord，并且break。（如果找的是第一个单词，做一些特殊处理后break)
	//	//标志：
	//	//   idx2层循环开始前置foundAllWord:=true; 找到即置为true;
	//	//   idx3层循环开始前置foundCurWord:=false; 找到即置为true;
	//	//   idx3层循环结束后，如果foundCurWord=false，则置foundAllWord:=false
	//	
	//	for($idx1=0;$idx1<count($wordInSentence)-count($mandatoryIndexList)+1;$idx1++){
	//		$foundAllWord=true;
	//		$foundFirstWord=false;
	//		$foundList = array();
	//		if ($fDebug)  echo "idx1=$idx1  ".$wordInSentence[$idx1]."</br>";
	//		for($idx2=0;$idx2<count($mandatoryIndexList);$idx2++){
	//			$curWord=$wordInPhrase[$mandatoryIndexList[$idx2]['self']];
	//			$curWordVariants = getWordVariants($curWord);
	//			$curWordOptionalType = getWordOptionalType($curWord);
	//			
	//			$foundCurWord=false;
	//			if ($fDebug)  echo "idx2=$idx2  word to find is ".$curWord."</br>";
	//			if($idx2==0){
	//				$idx3_start=$idx1;
	//				$idx3_end = count($wordInSentence)-count($mandatoryIndexList)+1+$idx2;
	//			} else{
	//				$idx3_end = min(count($wordInSentence)-count($mandatoryIndexList)+1+$idx2, $idx3_start+$maxDistance);
	//			}
	//			if ($fDebug)  echo "idx3_start=$idx3_start idx3_end=$idx3_end<br>";
	//			for($idx3=$idx3_start;$idx3<$idx3_end;$idx3++){
	//				if ($fDebug)  echo "idx3=$idx3  word in sentence is ".$wordInSentence[$idx3]."</br>";					
	//				if(in_array($wordInSentence[$idx3],$curWordVariants,true)){
	//					if ($fDebug) echo "*** found</br>";
	//					$foundCurWord=true;
	//					$foundList []= array("start"=>$idx3,"len"=>strlen($wordInSentence[$idx3]));
	//					if ($idx2==0){
	//						$foundFirstWord = true;
	//						$foundFirstWordPos = $idx3;
	//					}
	//					break;
	//				}
	//			}
	//			if(($foundCurWord==false) && ($curWordOptionalType=='mandatory')){
	//				$foundAllWord = false;
	//				break;
	//			}
	//			if($foundCurWord==true){
	//				$idx3_start=$idx3+1;
	//			}
	//		}
	//		if ($foundAllWord == true){
	//			break;
	//		}
	//		if ($foundFirstWord == true) {
	//			$idx1 = $foundFirstWordPos+1;
	//		}
	//	}
	//	
	//	if($foundAllWord==false){
	//		echo "false";
	//	}else{
	//		echo "found: ";
	//		$foundList2 = array();
	//		$idx2=0；
	//		//插入Optional, 未完成
	//		//for($idx3=0;$idx3<$foundList[0]['start'] && $idx2<count(firstOptionalIndexList);$idx3++){
	//		//	if (wordInSentence[$idx3]==wordInPhrase[firstOptionalIndexList[$idx2]]){
	//		//		$foundList2 []= array("start"=>$idx3,"len"=>strlen($wordInSentence[$idx3]));
	//		//	}
	//		//}
	//		//for($i=0;$i<count($foundList)-1;$i++){
	//		//	$foundList2 []= ($foundList[$i]);
	//		//	$idx3_start = ($i==0) ? 0 : $foundList[$i-1]['start']+1;
	//		//	$idx3_end = ($i==0) ? 0 : $foundList[$i-1]['start']+1;
	//		//	foreach($foundList[$i]['before'] as $option)
	//		//}
	//		
	//		
	//		$letterPosInSentence=array(0);
	//		for($i=1;$i<count($wordInSentence);$i++){
	//			$letterPosInSentence[$i] = $letterPosInSentence[$i-1] + strlen($wordInSentence[$i-1]) +1;
	//		}
	//		if ($fDebug)  var_dump($letterPosInSentence);
	//		if ($fDebug)  var_dump($foundList);
	//		for($i=0;$i<count($foundList);$i++){
	//			echo $foundList[$i]['start']."  ";
	//		}
    //
	//		for($i=0;$i<count($foundList);$i++){
	//			echo substr($sentence,$letterPosInSentence[$foundList[$i]['start']],strlen($wordInSentence[$foundList[$i]['start']]))." ";
	//		}
	//		echo "</br>";
	//	}
	//}
}

if (! function_exists('getWordOptionalType')) {
	function getWordOptionalType($word){
		return in_array($word,array('a','the','an',"one's",'his','her','my','your','our','their')) ? 'optional' : 'mandatory';
	}
}

//search a series of patterns and add decorations around each pattern found 
function addDecoration($string,$patterns,$leftDecoration,$rightDecoration){
	$debug = false;
	$patternCount = count($patterns);
	for($j=0;$j<$patternCount;$j++){
		$jointPatterns[$j] = "/";
	}
	for($i=$patternCount-1;$i>=0;$i--){
		for($j=0;$j<=$i;$j++){
			$jointPatterns[$j] = $patterns[$i].$jointPatterns[$j];
			if ($i==$j)
				$jointPatterns[$j] = "/".$jointPatterns[$j];
			else
				$jointPatterns[$j] = ".*".$jointPatterns[$j];
		}
	}
	if (preg_match($jointPatterns[0],$string)<>1)
		return false;
	//var_dump($jointPatterns);
	
	$delimeter = "+=+=";
	$delimeterLen = mb_strlen("+=+=");
	$return1 = $return2 = "";
	$curString = $string;
	for($j=0;$j<$patternCount;$j++){//$patternCount
		$r = preg_replace($jointPatterns[$j],"$1$delimeter$0$delimeter$2",$curString);
		$delimeter1Start = mb_strpos($r, $delimeter);
		$delimeter1End = $delimeter1Start + $delimeterLen - 1;
		$delimeter2Start = mb_strpos($r, $delimeter, $delimeter1End+1);
		$delimeter2End = $delimeter2Start + $delimeterLen - 1;
		
		$r1 = mb_substr($r,0,$delimeter1Start);
		$r0 = mb_substr($r,$delimeter1End+1,($delimeter2Start-1)-($delimeter1End+1)+1);
		$r2 = mb_substr($r,$delimeter2End+1);
		if ($j<$patternCount-1){
			$curString = mb_substr($r0,mb_strlen($patterns[$j]));
			$return1 .= ($r1.$leftDecoration.$patterns[$j].$rightDecoration);
			$return2  = ($r2.$return2);
		}
		else{
			if ($j==0){
				$return = $r1.$leftDecoration.$patterns[$j].$rightDecoration.$r2;
			} else{
				$return = $return1.($r1.$leftDecoration.$patterns[$j].$rightDecoration).$return2;
			}
		}
		if ($debug)  echo "r=$r</br> r1=$r1  r0=$r0  r2=$r2 curString=$curString return1=$return1 return2=$return2</br>";
		/*
		$curLeftLen = mb_strpos($curLeftRight,"+=+=")+1;
		$curLeft = mb_substr($curString,0,$curLeftLen);
		$return += ($curLeft + "*" + $curPattern + "*");
		if ($j<$patternCount-1)
			$curString = mb_substr($curString,$curLeftLen + $curPatternLen);
		echo "curLeft=$curLeft curString=$curString return=$return</br>";*/
	}
	//echo "return=$return</br>";
	return $return;
}

function addDecoration2($string,$startLenArray,$leftDecoration,$rightDecoration){
	if ($startLenArray[0]['start']>0){
		$decString = mb_substr($string,0,$startLenArray[0]['start']);
	}else
		$decString = "";
	foreach($startLenArray as $i=>$startLen){
		$decString .= (is_array($leftDecoration) ? $leftDecoration[$i]: $leftDecoration);
		$decString .= mb_substr($string,$startLen['start'],$startLen['len']);
		$decString .= (is_array($rightDecoration) ? $rightDecoration[$i]: $rightDecoration);
		$middleStartPos = $startLen['start']+$startLen['len'];
		if ($i<count($startLen)-1){
			$middleLen = $startLenArray[$i+1]['start']-$middleStartPos;
		}else{
			if ($middleStartPos+$middleLen<mb_strlen($string)){
				$middleLen = mb_strlen($string)-$middleStartPos;
			}
			else{
				$middleLen = -1;
			}
		}
		if ($middleLen>0)
			$decString .= mb_substr($string,$middleStartPos,$middleLen);
	}
	return $decString;
}

if (! function_exists('english_pos_to_word_pos')) {
	function english_pos_to_word_pos($english_pos, $sentence)
	{
		$punctures = array(",",".","?","!",":",'"',"'","-",";","[","]");
		$sentence2 = $sentence;
		foreach($punctures as $puncture){
			$sentence2 = str_replace($puncture," ",$sentence2);
		}
		$wordInSentence = explode(" ", $sentence2);	
		
		$start_len_array=explode(";",$english_pos);
		
		$posInSentenceOfFirstLetterOfFirstWord = explode(",",$start_len_array[0])[0] ;
		$posInSentenceOfFirstLetterOfLastWord =  explode(",",$start_len_array[count($start_len_array)-1])[0];
		
		$first_word_pos_in_sentence=$last_word_pos_in_sentence=-1;$letterPosInSentence=0;
		$firstFound=$lastFound=false;
	 	for($i=0;$i<count($wordInSentence);$i++){
	 		$letterPosInSentence += (mb_strlen($wordInSentence[$i])+1); //point to the 1st letter of the next word(notice the white space between the words) 
			if (($firstFound==false)&&($letterPosInSentence>$posInSentenceOfFirstLetterOfFirstWord)){
				$first_word_pos_in_sentence = $i;
				$firstFound=true;
			}
			if (($lastFound==false)&&($letterPosInSentence>$posInSentenceOfFirstLetterOfLastWord)){
				$last_word_pos_in_sentence = $i;
				$lastFound=true;
			}
	 	}
		
		$result[0] = $first_word_pos_in_sentence;
		$result[1] = $last_word_pos_in_sentence;
		return $result;
	}
}

use App\Models\Mysql\dict\dict;
if (! function_exists('getWordVariants')) {
	function getWordVariants($word){
		switch($word){
			case 'a':
				$variants = array('a','the','an');
				break;
			case 'an':
				$variants = array('an','the','a');
				break;
			case 'the':
				$variants = array('the','a','an');
				break;
			case "one's":
				$variants = array('his','her','my','your','our','their');
				break;
			default:
				$sql = "select * from dict where english_key=\"".addslashes($word)."\"";
				$lookupResult = dict::select($sql);
				$variants = array($word);
				if (!empty($lookupResult)){
					$variantTypeList = array('plural','past_tense','past_participle','present_participle','third_person_singular');
					foreach($variantTypeList as $variantType){
						if (!empty($lookupResult[0]->{$variantType})){
							$variantField = $lookupResult[0]->{$variantType};
							$variantList = explode(" ",$variantField);
							foreach($variantList as $v)
								$variants []= $v;
						}	
					}
				}
				break;
		}
		return $variants;
	}
}

if (! function_exists('minPos')) {
	function minPos($posArray){
		$myMax = 99999999;
		$myMin = $myMax;
		foreach($posArray as $pos){
			if ($pos===false)
				continue;
			if ($pos<$myMin)
				$myMin=$pos;
		}
		if ($myMin<$myMax)
			return $myMin;
		else
			return false;
	}
}

if (! function_exists('mb_rmv_substr')) {
	function mb_rmv_substr($str,$startOfRemoval,$lenOfRemoval){
		$retStr = "";
		$endOfStr= mb_strlen($str)-1;
		if ($startOfRemoval>0)
			$retStr = mb_substr($str,0,$startOfRemoval-1);
		$endOfRemoval = $startOfRemoval + $lenOfRemoval - 1;
		if ($endOfRemoval<$endOfStr)
			$retStr .= (mb_substr($str,$endOfRemoval+1,$endOfStr-$endOfRemoval));
		return $retStr;
	}
}	

if (! function_exists('mb_min_strpos')) {
	function mb_min_strpos($haystack,$needleArray,$offset)
	{
		$posArray=array();
		foreach($needleArray as $needle){
			$posArray []= mb_strpos($haystack,$needle,$offset);
		}
		$pos = minPos($posArray);
		return $pos;
	}
}

?>
